﻿#include "lpng.h"
#include "texture.h"
#include "array.h"
#include "render.h"

#include "png.h"
#include "pngconf.h"

#include <lua.h>
#include <lauxlib.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct pic_data
{
	int	width, height;	/* 尺寸 */
	int	bit_depth;		/* 位深 */
	int	flag;			/* 一个标志，表示是否有alpha通道 */

	uint8_t	*rgba;	/* 图片数组 */ // 使用一维数组
};

#define PNG_BYTES_TO_CHECK 4
#define HAVE_ALPHA	1
#define NO_ALPHA	0


int detect_png(FILE *pic_fp, struct pic_data *out)
/* 用于解码png图片 */
{
	if (pic_fp == NULL) /* 文件打开失败 */
		return 0;

	/* 初始化各种结构 */
	png_structp png_ptr;
	png_infop   info_ptr;
	char        buf[PNG_BYTES_TO_CHECK];
	int         temp;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
	info_ptr = png_create_info_struct(png_ptr);

	setjmp(png_jmpbuf(png_ptr)); // 这句很重要

	temp = fread(buf, 1, PNG_BYTES_TO_CHECK, pic_fp);
	temp = png_sig_cmp((png_const_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK);

	/*检测是否为png文件*/
	if (temp != 0) return 0;

	rewind(pic_fp);
	/*开始读文件*/
	png_init_io(png_ptr, pic_fp);
	// 读文件了
	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);

	int color_type, channels;

	/*获取宽度，高度，位深，颜色类型*/
	channels = png_get_channels(png_ptr, info_ptr);	/*获取通道数*/
	out->bit_depth = png_get_bit_depth(png_ptr, info_ptr);	/* 获取位深 */
	color_type = png_get_color_type(png_ptr, info_ptr);	/*颜色类型*/

	int i, j;
	int size, pos = 0;
	/* row_pointers里边就是rgba数据 */
	png_bytep* row_pointers;
	row_pointers = png_get_rows(png_ptr, info_ptr);
	out->width = png_get_image_width(png_ptr, info_ptr);
	out->height = png_get_image_height(png_ptr, info_ptr);

	size = out->width * out->height; /* 计算图片的总像素点数量 */

	if (channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
	{/*如果是RGB+alpha通道，或者RGB+其它字节*/
		size *= (4 * sizeof(uint8_t));	/* 每个像素点占4个字节内存 */
		out->flag = HAVE_ALPHA;				/* 标记 */
		out->rgba = (uint8_t*)malloc(size);
		if (out->rgba == NULL)
		{/* 如果分配内存失败 */
			fclose(pic_fp);
			puts("错误(png):无法分配足够的内存供存储数据!");
			return 0;
		}

		temp = (4 * out->width);/* 每行有4 * out->width个字节 */
		for (i = 0; i < out->height; i++)
		{
			for (j = 0; j < temp; j += 4)
			{/* 一个字节一个字节的赋值 */
				out->rgba[pos++] = row_pointers[i][j]; // red
				out->rgba[pos++] = row_pointers[i][j + 1]; // green
				out->rgba[pos++] = row_pointers[i][j + 2];   // blue
				out->rgba[pos++] = row_pointers[i][j + 3]; // alpha
			}
		}
	}
	else if (channels == 3 || color_type == PNG_COLOR_TYPE_RGB)
	{/* 如果是RGB通道 */
		size *= (3 * sizeof(uint8_t));	/* 每个像素点占3个字节内存 */
		out->flag = NO_ALPHA;				/* 标记 */
		out->rgba = (uint8_t*)malloc(size);
		if (out->rgba == NULL)
		{/* 如果分配内存失败 */
			fclose(pic_fp);
			puts("错误(png):无法分配足够的内存供存储数据!");
			return 0;
		}

		temp = (3 * out->width);/* 每行有3 * out->width个字节 */
		for (i = 0; i < out->height; i++)
		{
			for (j = 0; j < temp; j += 3)
			{/* 一个字节一个字节的赋值 */
				out->rgba[pos++] = row_pointers[i][j]; // red
				out->rgba[pos++] = row_pointers[i][j + 1]; // green
				out->rgba[pos++] = row_pointers[i][j + 2];   // blue
			}
		}
	}
	else return 0;

	/* 撤销数据占用的内存 */
	png_destroy_read_struct(&png_ptr, &info_ptr, 0);
	return 1;
}


static int loadtexture(lua_State *L) {
	int id = (int)luaL_checkinteger(L, 1);
	size_t sz = 0;
	const char * filename = luaL_checklstring(L, 2, &sz);
	ARRAY(char, tmp, sz + 5);
	sprintf(tmp, "%s.png", filename);
	FILE *png = fopen(tmp, "rb");
	if (png == NULL) {
		return luaL_error(L, "Can't open %s(.png)", filename);
	}

	struct pic_data pngdata;

	int ok = detect_png(png, &pngdata);

	if (png)
		fclose(png);

	if (!ok) {
		if (pngdata.rgba) {
			free(pngdata.rgba);
		}
		luaL_error(L, "Invalid file %s", filename);
	}

	int type = 0;
	if (pngdata.flag == HAVE_ALPHA) {
		type = TEXTURE_RGBA8;
	}
	else {
		type = TEXTURE_RGB;
	}

	const char * err = texture_load(id, (enum TEXTURE_FORMAT)type, pngdata.width, pngdata.height, pngdata.rgba, lua_toboolean(L, 3));
	free(pngdata.rgba);
	if (err) {
		return luaL_error(L, "%s", err);
	}

	return 0;
}
static int loadpng(lua_State *L) {
	return 0;
}
static int unload_tex(lua_State *L) {
	int id = (int)luaL_checkinteger(L, 1);
	texture_unload(id);
	return 0;
}
int ejoy2d_png(lua_State *L) {
	luaL_Reg l[] = {
			{ "texture", loadtexture },
			{ "load", loadpng },
			{ "unload", unload_tex },
			{ NULL, NULL },
	};

	luaL_newlib(L, l);

	return 1;
}


